<script src="utils.js"></script>
<script src="data.js"></script>
<script src="decimal.js"></script>
<script>
	// 数据
	function TData(k, v, f) {
		this.key = k;
		this.val = v;
		this.fmt = f;
	}
	// 任务参数, 测量点（信息点）, Fn信息类 ,数据
	function TPara(da, dt, datas) {
		this.da = da;
		this.dt = dt;
		this.datas = datas;
	}
	TPara.prototype.add = function(k, v, f) {
		if (!this.datas) this.datas = [];
		this.datas.push(new TData(k, v, f));
	}
	// 任务结果, 标题, 数据
	function TResult(title, datas) {
		this.title = title;
		this.datas = datas;
	}
	TResult.prototype.add = function(k, v, f) {
		if (!this.datas) this.datas = [];
		this.datas.push(new TData(k, v, f));
	}
	//任务 终端地址, AFN, 任务参数数组
	function Task(commAddr, afn, paras) {
		this.commAddr = commAddr;
		this.afn = afn;
		if (isArray(paras)) {
			this.paras = paras;
		} else {
			this.paras = [];
			if (paras) this.paras.push(paras);
		}
	}

	// 规约类
	function UpGw() {
		this.MIN_PACKET_LEN = 16;
		this.MAX_PACKET_LEN = 1024;
		this.HEAD_CHAR = 0x68;
		this.END_CHAR = 0x16;

		this.LEN_POS = 1;
		this.HEAD_POS = 5;
		this.CC_POS = 6;
		this.ADDR_POS = 7;
		this.DATA_LEN_POS = 10;
		this.SEQ_POS = 13;
		this.DATA_POS = 14;
	}
	// 序列号
	UpGw.SeqCode = {
		SimpleT0: 0x70,
		Con0T0: 0x60
	}
	//控制码
	UpGw.CtrlCode = {
		GetData2: 0x4b,
		GetData1: 0x4A
	}
	//功能码
	UpGw.Afn = {
		Control: 05,
		GetConfig: 09,
		GetPara: 10,
		GetData1: 12,
		GetData2: 13,
		GetData3: 14,
		properties: {
			10: {
				cc: UpGw.CtrlCode.GetData2,
				sc: UpGw.SeqCode.SimpleT0,
				name: "查询参数"
			},
			12: {
				cc: UpGw.CtrlCode.GetData1,
				sc: UpGw.SeqCode.Con0T0,
				name: "请求1类数据"
			},
			13: {
				cc: UpGw.CtrlCode.GetData2,
				sc: UpGw.SeqCode.Con0T0,
				name: "请求2类数据"
			},
		}
	};
	//用户类别
	UpGw.UserType = {
		Unknown: 0,
		Resident: 5,
		Trans: 6,
		properties: {
			6: {
				name: "考核计量点"
			},
			5: {
				name: "居民用户"
			},
			0: {
				name: "无需设置"
			},
		}
	};
	// 报文检查,返回有效帧,否则返回undefined
	UpGw.prototype.searchValid = function(buffer, len) {
		if (len < this.MIN_PACKET_LEN) return null;
		for (i = 0; i <= len - this.MIN_PACKET_LEN; i++) {
			if ((buffer[i + this.CC_POS] >> 7) == 1 &&
				buffer[i] == this.HEAD_CHAR &&
				buffer[i + this.HEAD_POS] == this.HEAD_CHAR) {
				var dataLen = (buffer[i + this.LEN_POS] >> 2) + buffer[i + 1 + this.LEN_POS] * 64;
				var packetEnd = i + dataLen + 7;
				if (len > packetEnd && buffer[packetEnd] == this.END_CHAR) {
					var crc = buffer[packetEnd - 1];
					if (Util.sumCheck(buffer, i + this.CC_POS, i + this.CC_POS + dataLen - 1) == crc) {
						return buffer.subarray(i, packetEnd + 1);
					}
				}
			}
		}
	}
	UpGw.prototype.getCommAddr = function(xb) {
		let commAddr = xb.getBcdStr(this.ADDR_POS, 2);
		commAddr += xb.get(Fmt.Bcd2, this.ADDR_POS + 2);
		return commAddr;
	}
	//根据任务组包
	UpGw.prototype.build = function(task) {
		let xb = new XBuffer(new ArrayBuffer(2024));
		let areaNo = parseInt(task.commAddr.substring(0, 4), 16);
		let termNo = parseInt(task.commAddr.substring(4), 16);

		xb.set(this.HEAD_CHAR);
		xb.set(0, Fmt.Bin4);
		xb.set(this.HEAD_CHAR);
		xb.set(UpGw.Afn.properties[task.afn].cc);
		xb.set(areaNo, Fmt.Bin2);
		xb.set(termNo, Fmt.Bin2);
		xb.set(2); //MasterA3
		xb.set(task.afn);
		xb.set(UpGw.Afn.properties[task.afn].sc);
		for (let i = 0; i < task.paras.length; i++) {
			let para = task.paras[i];
			xb.set(para.da, Fmt.Da);
			xb.set(para.dt, Fmt.Dt);
			if (!para.datas) continue;
			for (let d = 0; d < para.datas.length; d++) {
				let data = para.datas[d];
				xb.set(data.val, data.fmt);
			}
		}
		let len = ((xb.pos - this.CC_POS) << 2) + 2;
		xb.set(len, Fmt.Bin2, this.LEN_POS);
		xb.set(len, Fmt.Bin2, this.LEN_POS + 2);
		let crc = Util.sumCheck(xb.bytes, this.CC_POS, xb.pos);
		xb.set(crc);
		xb.set(this.END_CHAR);
		return xb.bytes.subarray(0, xb.pos);
	}
	//根据任务解析
	UpGw.prototype.parse = function(task, xb) {
		var afn = xb.get(Fmt.Bin1, this.ADDR_POS + 5);
		var addr = this.getCommAddr(xb);
		var result = new TResult(UpGw.Afn.properties[afn].name + " " + addr);
		xb.pos = this.DATA_POS;
		while (xb.bytes.length - xb.pos > 5) {
			var da = xb.get(Fmt.Da);
			var dt = xb.get(Fmt.Dt);
			switch (afn) {
				case UpGw.Afn.GetData1:
					switch (dt) {
						case 2:
							{
								result.add("终端日历时钟", xb.get(Fmt.Data1));
							}
							break;
					}
					break;
				case UpGw.Afn.GetPara:
					switch (dt) {
						case 10:
							{
								let c = xb.get(Fmt.Bin2);
								result.add("配置数量", c);
								for (let i = 1; i <= c; i++) {
									xb.pos += 2;
									result.add("测量点号" + i, xb.get(Fmt.Bin2));
									xb.pos += 2;
									result.add("通信地址" + i, xb.get(Fmt.Bcd6));
									xb.pos += 14;
									let mutype = xb.get(Fmt.Bin1);
									let ut = (mutype & 0xF0) >> 4;
									let mt = (mutype & 0x0F);
									result.add("用户类别" + i, UpGw.UserType.properties[ut].name);
								}
							}
							break;
					}
					break;
			}
		}
		return result;
	}



	var up = new UpGw();
	var buffer1;

	let para = new TPara(0, 10);
	para.add("数量", 2, Fmt.Bin2);
	para.add("序号1", 1, Fmt.Bin2);
	para.add("序号2", 3, Fmt.Bin2);
	para.add("序号2", 4, Fmt.Bin2);
	para.add("序号2", 5, Fmt.Bin2);
	var task = new Task("19008800", UpGw.Afn.GetPara, para);
	buffer1 = up.build(task)
	log("build " + Util.bytesToStr(buffer1));



	var xb = new XBuffer(Util.strToBytes(
		"11 22 68 12 01 12 01 68 88 00 19 00 88 02 0A 60 00 00 02 01 02 00 01 00 01 00 01 02 11 11 11 11 11 11 00 00 00 00 00 00 04 03 00 00 00 00 00 00 65 02 00 02 00 7C 1E 03 68 57 06 15 00 00 00 00 00 00 00 04 09 00 00 00 00 00 00 5B 54 16"
	));
	// log("xb 0 = " + xb.bytes[0]);
	// log("xb 0 = " + xb.get());
	// log("xb 0 = " + xb.get(Fmt.Bin1, 14));
	// log("xb 0 = " + Util.bytesToStr(xb.bytes.subarray(1, 3)));
	buffer1 = up.searchValid(xb.bytes, xb.bytes.length);

	if (buffer1) log("valid = " + Util.bytesToStr(buffer1));
	xb = new XBuffer(buffer1);
	var result = up.parse(task, xb);
	log("parse " + result.title);
	if (result.datas) {
		for (let i = 0; i < result.datas.length; i++) {
			let data = result.datas[i];
			log("parse " + data.key + " " + data.val);
		}
	}
	var a = Decimal.div("20", 6)
	log("build {0}".format(a.toFixed(4)));
	log("build {0}".format(a.toPrecision(4)));
</script>
